
// Define one of:
// MODE_640x480, MODE_800x600, MODE_1024x768, MODE_1280x1024.
// ("physical mode" sent to the HDMI)

`include "TMDS_encoder.v"

// Generate HDMI signal from VGA signal
module GFX_hdmi(
    input wire 	      pixel_clk,    // pixel clock
    input wire 	      pixel_clk_x5, // 5 times pixel clock freq (used by TMDS serializer)
	                            // The TMDS serializers operate at (pixel_clock_freq * 10), 
	                            // but we use DDR mode, hence (pixel_clock_freq * 5).
    input wire [7:0]  R,
    input wire [7:0]  G,
    input wire [7:0]  B,
    input wire 	      hsync,
    input wire 	      vsync,
    input wire        draw_area,		
	
    output wire [3:0] gpdi_dp // HDMI signals, blue, green, red, clock 	
		              // dgpi_dn generated by pins (see, e.g., ulx3s.lpf)
);
   
   // RGB TMDS encoding
   // Generate 10-bits TMDS red,green,blue signals. Blue embeds HSync/VSync in its 
   // control part.
   wire [9:0] TMDS_R, TMDS_G, TMDS_B;
   TMDS_encoder encode_R(.clk(pixel_clk), .VD(R), .CD(2'b00)        , .VDE(draw_area), .TMDS(TMDS_R));
   TMDS_encoder encode_G(.clk(pixel_clk), .VD(G), .CD(2'b00)        , .VDE(draw_area), .TMDS(TMDS_G));
   TMDS_encoder encode_B(.clk(pixel_clk), .VD(B), .CD({vsync,hsync}), .VDE(draw_area), .TMDS(TMDS_B));

   // Modulo-5 clock divider.
   reg [4:0] TMDS_mod5=1;
   wire      TMDS_shift_load = TMDS_mod5[4];
   always @(posedge pixel_clk_x5) TMDS_mod5 <= {TMDS_mod5[3:0],TMDS_mod5[4]};
   
   // Shifters
   // Every 5 clocks, we get a fresh R,G,B triplet from the TMDS encoders,
   // else we shift.
   reg [9:0] TMDS_shift_R=0, TMDS_shift_G=0, TMDS_shift_B=0;
   always @(posedge pixel_clk_x5) begin
      TMDS_shift_R <= TMDS_shift_load ? TMDS_R : {2'b00,TMDS_shift_R[9:2]};
      TMDS_shift_G <= TMDS_shift_load ? TMDS_G : {2'b00,TMDS_shift_G[9:2]};
      TMDS_shift_B <= TMDS_shift_load ? TMDS_B : {2'b00,TMDS_shift_B[9:2]};	
   end

   // DDR serializers: they send D0 at the rising edge and D1 at the falling edge.
`ifndef BENCH_OR_LINT
 `ifdef ULX3S
   ODDRX1F ddr_R (.D0(TMDS_shift_R[0]), .D1(TMDS_shift_R[1]), .Q(gpdi_dp[2]), .SCLK(pixel_clk_x5), .RST(1'b0));
   ODDRX1F ddr_G (.D0(TMDS_shift_G[0]), .D1(TMDS_shift_G[1]), .Q(gpdi_dp[1]), .SCLK(pixel_clk_x5), .RST(1'b0));
   ODDRX1F ddr_B (.D0(TMDS_shift_B[0]), .D1(TMDS_shift_B[1]), .Q(gpdi_dp[0]), .SCLK(pixel_clk_x5), .RST(1'b0));
 `endif
`endif
   
   // The pixel clock is sent through the fourth differential pair.
   assign gpdi_dp[3] = pixel_clk;
		
endmodule
  
/**************************************************************************************/   

`ifdef BENCH_OR_LINT

module GFX_PLL(
    input  wire pclk,         // the board's clock
    output wire pixel_clk,    // pixel clock
    output wire pixel_clk_x5  // 5 times pixel clock freq (used by TMDS serializer)
);
   assign pixel_clk = pclk;
   assign pixel_clk_x5 = pclk;
endmodule

`else 

`ifdef ULX3S

module GFX_PLL(
    input  wire pclk,         // the board's clock
    output wire pixel_clk,    // pixel clock
    output wire pixel_clk_x5  // 5 times pixel clock freq (used by TMDS serializer)
	                      // The TMDS serializers operate at (pixel_clock_freq * 10), 
	                      // but we use DDR mode, hence (pixel_clock_freq * 5).
);

 // The parameters of the PLL, 
 // They are found by using: ecppll -i 25 -o <5*pixel_clock> -f foobar.v
   
 `ifdef MODE_640x480
   localparam CLKI_DIV = 1;
   localparam CLKOP_DIV = 5;
   localparam CLKOP_CPHASE = 2;
   localparam CLKOP_FPHASE = 0;
   localparam CLKFB_DIV = 5;
 `endif

 `ifdef MODE_800x600
   localparam CLKI_DIV = 1;
   localparam CLKOP_DIV = 3;
   localparam CLKOP_CPHASE = 1;
   localparam CLKOP_FPHASE = 0;
   localparam CLKFB_DIV = 8;
 `endif
   
 `ifdef MODE_1024x768
   localparam CLKI_DIV = 1;
   localparam CLKOP_DIV = 2;
   localparam CLKOP_CPHASE = 1;
   localparam CLKOP_FPHASE = 0;
   localparam CLKFB_DIV = 13;
 `endif

 `ifdef MODE_1280x1024
   localparam CLKI_DIV = 3;
   localparam CLKOP_DIV = 1;
   localparam CLKOP_CPHASE = 0;
   localparam CLKOP_FPHASE = 0;
   localparam CLKFB_DIV = 65;
 `endif

   // The PLL converts a 25 MHz clock into a (pixel_clock_freq * 5) clock
   // The (half) TMDS serializer clock is generated on pin CLKOP. 
   // In addition, the pixel clock (at TMDS freq/5) is generated on 
   // pin CLKOS (hence CLKOS_DIV = 5*CLKOP_DIV).
   (* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
   EHXPLLL #(
        .CLKI_DIV(CLKI_DIV),
        .CLKOP_DIV(CLKOP_DIV),
        .CLKOP_CPHASE(CLKOP_CPHASE),
        .CLKOP_FPHASE(CLKOP_FPHASE),
	.CLKOS_ENABLE("ENABLED"),
	.CLKOS_DIV(5*CLKOP_DIV),
	.CLKOS_CPHASE(CLKOP_CPHASE),
	.CLKOS_FPHASE(CLKOP_FPHASE),
        .CLKFB_DIV(CLKFB_DIV)
   ) pll_i (
        .CLKI(pclk),
        .CLKOP(pixel_clk_x5),
        .CLKFB(pixel_clk_x5),
	.CLKOS(pixel_clk),
        .PHASESEL0(1'b0),
        .PHASESEL1(1'b0),
        .PHASEDIR(1'b1),
        .PHASESTEP(1'b1),
        .PHASELOADREG(1'b1),
        .PLLWAKESYNC(1'b0),
        .ENCLKOP(1'b0)
   );

endmodule

`endif
`endif
